parser grammar SinSam;

/*
@rulecatch { 
  catch (RecognitionException e) {
    throw e;
  }
}


@members {
protected void mismatch(IntStream input, int ttype, BitSet follow)
  throws RecognitionException
{
  throw new MismatchedTokenException(ttype, input);
}

public void recoverFromMismatchedSet(IntStream input, RecognitionException e, BitSet follow)
  throws RecognitionException
{
  throw e;
}
} 
*/



options {
  language = Java;
  tokenVocab = LexSam;
  //backtrack = true; //para resolver ambiguedades
  //k = 3;
}

/*@header {
   import 
}*/

@members {
	public SAM sam = new SAM();
}


entrada: { System.out.println("\nMaquina SAM: \n Leyendo fichero...");}
        (etiqueta | instruccion)* EOF 
        
        { System.out.println("Traduciendo etiquetas..."); 
        //actualiza instrucciones call de etiquetas de usuario: 
        //sustituye id temporal de etiqueta que esta en despl por direccion de etiqueta
        sam.translateLabels();
        //System.out.println("Segmento de codigo");
        //sam.printCodeSegment(); 
        System.out.println("Ejecutando...");
        sam.executeAll(); //inicializa CP, etc y ejecuta todas las instrucciones
        };

numero returns [byte value]: 
    num=NUM_INTEGER { $numero.value = Byte.parseByte($num.getText()); };
  
npalabras returns [byte value]:  // (n)
         LPARENTHESIS numero RPARENTHESIS { $npalabras.value = $numero.value; }
       | LPARENTHESIS reg RPARENTHESIS { $npalabras.value = $reg.value; };
         
etiqueta: id=IDENT COLON { sam.insertUserLabel($id.getText()); }; //asocia direccion gracias a CP*/ };

despl returns [byte value]: //d
    numero  { $despl.value = $numero.value; } 
    | MINUS numero { $despl.value = (byte) - $numero.value; };

reg returns [byte value]:
   | CB { $reg.value = Registro.CB; }
   | CT { $reg.value = Registro.CT; }
   | PB { $reg.value = Registro.PB; }
   | PT { $reg.value = Registro.PT; }
   | SB { $reg.value = Registro.SB; }
   | ST { $reg.value = Registro.ST; }
   | HB { $reg.value = Registro.HB; }
   | HT { $reg.value = Registro.HT; }
   | LB { $reg.value = Registro.LB; }
   | L1 { $reg.value = Registro.L1; }
   | L2 { $reg.value = Registro.L2; }
   | L3 { $reg.value = Registro.L3; }
   | L4 { $reg.value = Registro.L4; }
   | L5 { $reg.value = Registro.L5; }
   | L6 { $reg.value = Registro.L6; }
   | CP { $reg.value = Registro.CP; };
   

instruccion: calli | jumpi | halt | push | loadl | loadi | storei |
             loada | jump | returnar | pop | load | store | call | jumpif;

calli: CALLI { Instruccion i = new Instruccion(Instruccion.CALLI, (byte) 0, (byte) 0, (byte) 0); sam.pushInstruction(i); };
jumpi: JUMPI { Instruccion i = new Instruccion(Instruccion.JUMPI, (byte) 0, (byte) 0, (byte) 0); sam.pushInstruction(i); };
halt: HALT { Instruccion i = new Instruccion(Instruccion.HALT, (byte) 0, (byte) 0, (byte) 0); sam.pushInstruction(i); };

//d
push: PUSH despl 
   { Instruccion i = new Instruccion(Instruccion.PUSH, (byte) 0, (byte) 0, $despl.value); 
   	sam.pushInstruction(i); };
   
loadl: LOADL despl
  { Instruccion i = new Instruccion(Instruccion.LOADL, (byte) 0, (byte) 0, $despl.value); 
  	sam.pushInstruction(i); };

// (n)
loadi: LOADI npalabras 
   { Instruccion i = new Instruccion(Instruccion.LOADI, (byte) 0, $npalabras.value, (byte) 0); 
	sam.pushInstruction(i); } ;

storei: STOREI npalabras   
   { Instruccion i = new Instruccion(Instruccion.STOREI, (byte) 0, $npalabras.value, (byte) 0); 
    sam.pushInstruction(i); } ; 

// d[r]
loada: LOADA despl LBRACKET reg RBRACKET   
    { Instruccion i = new Instruccion(Instruccion.LOADA, $reg.value, (byte) 0, $despl.value); 
    sam.pushInstruction(i); } ;
    
jump: JUMP despl LBRACKET reg RBRACKET
    { Instruccion i = new Instruccion(Instruccion.JUMP, $reg.value, (byte) 0, $despl.value); 
    sam.pushInstruction(i); } ;


//(n) d 
returnar: RETURN npalabras despl
   { Instruccion i = new Instruccion(Instruccion.RETURN, (byte) 0, $npalabras.value, $despl.value); 
    sam.pushInstruction(i); } ;  
    
pop: POP npalabras despl   
    { Instruccion i = new Instruccion(Instruccion.POP, (byte) 0, $npalabras.value, $despl.value); 
    sam.pushInstruction(i); } ;

 // (n) d[r]
load: LOAD npalabras despl LBRACKET reg RBRACKET
    { Instruccion i = new Instruccion(Instruccion.LOAD, $reg.value, $npalabras.value, $despl.value); 
    sam.pushInstruction(i); } ;
    
store: STORE npalabras despl LBRACKET reg RBRACKET
    { Instruccion i = new Instruccion(Instruccion.STORE, $reg.value, $npalabras.value, $despl.value); 
    sam.pushInstruction(i); } ;
    
call: CALL npalabras despl LBRACKET reg RBRACKET
    { Instruccion i = new Instruccion(Instruccion.CALL, $reg.value, $npalabras.value, $despl.value); 
    sam.pushInstruction(i); }
    
    | CALL npalabras id=IDENT
    
    {
    	Instruccion i;
    	byte prim = Primitiva.getPrimitiveID($id.getText()); //numero de primitiva
    	if (prim == 0) {// no es una primitiva -> es una etiqueta de usuario
    	   byte idTemp = sam.insertTempLabel($id.getText()); //comprueba si se ha insertado. en cualquier caso devuelve id
    	   //XXX: atencion: el registro podria ser LB, L1, etc. seria un switch??
    	   i = new Instruccion(Instruccion.CALL, Registro.LB, $npalabras.value, idTemp); //el despl es temporal: se actualizara despues a dir de etiqueta
    	}
    	else //es una primitiva: despl = label
			i = new Instruccion(Instruccion.CALL, Registro.PB, $npalabras.value, prim); 
            
        sam.pushInstruction(i);
    };
    
    
jumpif: JUMPIF npalabras despl LBRACKET reg RBRACKET
    { Instruccion i = new Instruccion(Instruccion.JUMPIF, $reg.value, $npalabras.value, $despl.value); 
    sam.pushInstruction(i); } ;




